if ( rmrr->base_address >= rmrr->end_address )
{
- dprintk(XENLOG_ERR VTDPREFIX, "RMRR is incorrect.\n");
+ dprintk(XENLOG_ERR VTDPREFIX,
+ "RMRR error: base_addr %"PRIx64" end_address %"PRIx64"\n",
+ rmrr->base_address, rmrr->end_address);
return -EFAULT;
}
remap_rte = (struct IO_APIC_route_remap_entry *) &old_rte;
- if ( remap_rte->format == 0 )
+ if ( (remap_rte->format == 0) || (old_rte.delivery_mode == dest_SMI) )
{
*IO_APIC_BASE(apic) = rte_upper ? (reg + 1) : reg;
return *(IO_APIC_BASE(apic)+4);
remap_rte = (struct IO_APIC_route_remap_entry *) &old_rte;
+ if ( old_rte.delivery_mode == dest_SMI )
+ {
+ /* Some BIOS does not zero out reserve fields in IOAPIC
+ * RTE's. clear_IO_APIC() zeroes out all RTE's except for RTE
+ * with MSI delivery type. This is a problem when the host
+ * OS converts SMI delivery type to some other type but leaving
+ * the reserved field uninitialized. This can cause interrupt
+ * remapping table out of bound error if "format" field is 1
+ * and the "index" field has a value that that is larger than
+ * the maximum index of interrupt remapping table.
+ */
+ if ( remap_rte->format == 1 )
+ {
+ remap_rte->format = 0;
+ *IO_APIC_BASE(apic) = reg;
+ *(IO_APIC_BASE(apic)+4) = *(((u32 *)&old_rte)+0);
+ *IO_APIC_BASE(apic) = reg + 1;
+ *(IO_APIC_BASE(apic)+4) = *(((u32 *)&old_rte)+1);
+ }
+
+ *IO_APIC_BASE(apic) = rte_upper ? (reg + 1) : reg;
+ *(IO_APIC_BASE(apic)+4) = value;
+ return;
+ }
+
/* mask the interrupt while we change the intremap table */
saved_mask = remap_rte->mask;
remap_rte->mask = 1;